package com.hitqz.robot.biz.controller;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import com.google.common.util.concurrent.RateLimiter;
import com.hitqz.robot.api.business.dto.RobotControlButtonDto;
import com.hitqz.robot.api.business.enums.RobotOtherCommandEnum;
import com.hitqz.robot.api.business.model.vo.robot.RobotExecuteCmdOptionVo;
import com.hitqz.robot.api.common.dto.robot.RobotMoveDto;
import com.hitqz.robot.api.common.entity.Robot;
import com.hitqz.robot.api.common.entity.RobotMap;
import com.hitqz.robot.api.common.entity.RobotMapPos;
import com.hitqz.robot.biz.aop.RemoteControlAuthTag;
import com.hitqz.robot.biz.business.dispatch.DispatchService;
import com.hitqz.robot.biz.service.BusinessService;
import com.hitqz.robot.biz.service.RobotMapPosService;
import com.hitqz.robot.biz.service.RobotMapService;
import com.hitqz.robot.biz.service.RobotService;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.driver.model.MapAttributesDto;
import com.hitqz.robot.driver.parent.RobotDriverService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xupkun
 * @date 2024/6/15
 */

@RestController
@AllArgsConstructor
@RequestMapping("/ctrRobot")
@Tag(name = "机器人控制")
@Slf4j
public class CtrRobotController {

    private final RobotDriverService robotDriverService;

    private final RobotMapPosService robotMapPosService;

    private final RobotMapService robotMapService;

    private final RobotService robotService;

    private final BusinessService businessService;

    private final DispatchService dispatchService;

    private final RateLimiter limiter = RateLimiter.create(5);

    private final RateLimiter powerChangeLimiter = RateLimiter.create(1);

    private final ThreadPoolTaskExecutor ctrlExecutor;

    @Operation(summary = "按钮控制")
    @PostMapping(value={"/button"} )
    public R button(@RequestBody RobotControlButtonDto button) {
        if (StrUtil.isBlank(button.getAction())){
            return R.failed();
        }
        if (button.getAction().contains("powerOnOrOff") && !powerChangeLimiter.tryAcquire(10)){
            log.warn("远程开关机操作频繁");
            return R.failed("操作频繁,请稍后重试");
        }
        if (limiter.tryAcquire(1)) {
            log.info("web button: {}", button.getAction());
            SpringUtil.getApplicationContext().publishEvent(button);
        }
        return R.ok();
    }

    @Operation(summary = "按钮控制")
    @PostMapping(value={"/button/{source}"} )
    @RemoteControlAuthTag
    public R buttonSource(@RequestBody RobotControlButtonDto button,
                    @PathVariable(required = false)@DefaultValue(value = "pc") String source) {
        if (StrUtil.isBlank(button.getAction())){
            return R.failed();
        }
        if (button.getAction().contains("powerOnOrOff") && !powerChangeLimiter.tryAcquire(10)){
            log.warn("远程开关机操作频繁");
            return R.failed("操作频繁,请稍后重试");
        }
        if (limiter.tryAcquire(1)) {
            log.info("web button: {}", button.getAction());
            SpringUtil.getApplicationContext().publishEvent(button);
        }
        return R.ok();
    }

    @GetMapping(value = "executeCmd/options")
    public R<List<RobotExecuteCmdOptionVo>> getExecuteCmdOptions(){
        List<RobotExecuteCmdOptionVo> list = new ArrayList<>();
        Arrays.stream(RobotOtherCommandEnum.values()).forEach(cmd -> {
            RobotExecuteCmdOptionVo robotExecuteCmdOptionVo = new RobotExecuteCmdOptionVo();
            robotExecuteCmdOptionVo.setCmdName(cmd.getName());
            robotExecuteCmdOptionVo.setCmdDesc(cmd.getDes());
            list.add(robotExecuteCmdOptionVo);
        });
        return R.ok(list);
    }

    @GetMapping(value = "executeCmd/{cmdName}")
    public R executeCmd(@PathVariable(value = "cmdName") String cmdName) {
        RobotOtherCommandEnum cmd = RobotOtherCommandEnum.getByName(cmdName);
        if (cmd == null){
            return R.failed("命令不存在");
        }
        return robotDriverService.executeOtherCommand(cmd,null);
    }


    @GetMapping(value = "getNavPath")
    public R getNavPath(Integer sourceId, Integer targetId) {
        return R.ok(robotDriverService.getNavPath(
                robotMapPosService.getById(sourceId)
                , robotMapPosService.getById(targetId), robotMapPosService.listByMap(null)));
    }

    @GetMapping(value = "getMapPosFromRobot")
    @Operation(summary = "同步地图点位")
    public R getMapPos(@RequestParam(value = "mapCode") String mapCode) {
        List<RobotMapPos> robotMapPosList = robotDriverService.getRobotMapPos(mapCode);
        if (robotMapPosList==null || robotMapPosList.isEmpty()){
            return R.failed("暂无数据");
        }
        RobotMap robotMap = robotMapService.findByMapCode(mapCode);
        if (robotMap==null){
            return R.failed("未找到相应地图数据");
        }
        List<RobotMapPos> list = robotMapPosService.list(Wrappers.<RobotMapPos>lambdaQuery().eq(RobotMapPos::getMapCode, mapCode));
        Map<String, RobotMapPos> dbMap = list.stream().collect(Collectors.toMap(RobotMapPos::getCode, obj -> obj));
        for (RobotMapPos robotMapPos : robotMapPosList) {
            RobotMapPos posDb = dbMap.get(robotMapPos.getCode());
            robotMapPos.setMapName(robotMap.getMapName());
            robotMapPos.setMapCode(mapCode);
            if (posDb==null) {
                robotMapPosService.save(robotMapPos);
            }else {
                robotMapPos.setId(posDb.getId());
                robotMapPosService.updateById(robotMapPos);
            }
        }
        return R.ok();
    }


    @GetMapping(value = "getMapListFromRobot")
    @Operation(summary = "同步地图数据")
    public R getMapList() {
        List<RobotMap> robotMaps = robotDriverService.getMapList();
        if (robotMaps==null || robotMaps.isEmpty()){
            return R.failed("暂无数据");
        }
        for (RobotMap robotMap : robotMaps) {
            RobotMap mapDb = robotMapService.findByMapCode(robotMap.getMapCode());
            MapAttributesDto mapAttributesDto = robotDriverService.getMapAttributes(robotMap.getMapCode());
            robotMap.setMapOriginx(Float.valueOf(String.valueOf(mapAttributesDto.getMapOriginx())));
            robotMap.setMapOriginy(Float.valueOf(String.valueOf(mapAttributesDto.getMapOriginy())));
            robotMap.setMapResolution(Float.valueOf(String.valueOf(mapAttributesDto.getMapResolution())));
            robotMap.setType(1);
            if (mapDb==null) {
                robotMapService.save(robotMap);
            }else {
                robotMap.setId(mapDb.getId());
                robotMapService.updateById(robotMap);
            }
        }
        Robot robot = robotService.getDefaultRobot();
        String mapCode = robotDriverService.getCurrentMap();
        robot.setMapCode(mapCode);
        robotService.updateById(robot);
        return R.ok(robotMaps);
    }


    @GetMapping(value = "goCharge")
    public R goCharge() {
        businessService.goCharge(true, false);
        return R.ok();
    }


    @GetMapping(value = "getMapData")
    public R getMapData() {
        return R.ok( robotDriverService.getMapAttributes("inner_1"));
    }


    @PostMapping(value = "setSpeed")
    @Operation(summary = "速度控制-使用摇杆")
    public R setSpeed(@RequestBody RobotMoveDto baseSpeedDto){
        //给安卓的摇杆
        baseSpeedDto.setRocker(true);
        if (limiter.tryAcquire(1)) {
            ctrlExecutor.submit(()->{
                robotDriverService.setSpeed(baseSpeedDto);
            });
            log.info("speed:line:{},angular:{}", baseSpeedDto.getLinerSpeed(), baseSpeedDto.getAngularSpeed());
        }
        return R.ok();
    }

}
